/*
* Copyright (c) 2019-2022, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//!
//! \file        mos_utilities_specific.cpp
//! \brief       This module implements the MOS wrapper functions for Linux/Android
//!

#include <fcntl.h>     // open
#include <stdlib.h>    // atoi
#include <string.h>    // strlen, strcat, etc.
#include <errno.h>     // strerror(errno)
#include <time.h>      // get_clocktime
#include <dlfcn.h>     // dlopen, dlsym, dlclose
#include <unistd.h>
#include <signal.h>
#include <unistd.h>  // fork
#include <algorithm>
#include <sys/types.h>
#include <sys/stat.h>  // fstat
#include <sys/ipc.h>  // System V IPC
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include "mos_compat.h" // libc variative definitions: backtrace
#include "mos_user_setting.h"
#include "mos_utilities_specific.h"
#include "mos_utilities.h"
#include "mos_util_debug.h"
#include "inttypes.h"

int32_t g_mosMemAllocCounter         = 0;
int32_t g_mosMemAllocFakeCounter     = 0;
int32_t g_mosMemAllocCounterGfx      = 0;
uint8_t g_mosUltFlag                 = 0;
int32_t g_mosMemAllocIndex           = 0;

int32_t *MosUtilities::m_mosMemAllocCounter       = &g_mosMemAllocCounter;
int32_t *MosUtilities::m_mosMemAllocFakeCounter   = &g_mosMemAllocFakeCounter;
int32_t *MosUtilities::m_mosMemAllocCounterGfx    = &g_mosMemAllocCounterGfx;
uint8_t *MosUtilities::m_mosUltFlag               = &g_mosUltFlag;
int32_t *MosUtilities::m_mosMemAllocIndex         = &g_mosMemAllocIndex;

const char           *MosUtilitiesSpecificNext::m_szUserFeatureFile     = USER_FEATURE_FILE;
MOS_PUF_KEYLIST      MosUtilitiesSpecificNext::m_ufKeyList              = nullptr;

#if (_DEBUG || _RELEASE_INTERNAL)
int32_t g_mosMemoryFailSimulateAllocCounter = 0;
int32_t *MosUtilities::m_mosAllocMemoryFailSimulateAllocCounter = &g_mosMemoryFailSimulateAllocCounter;
#endif

double MosUtilities::MosGetTime()
{
    struct timespec ts = {};
    clock_gettime(CLOCK_REALTIME, &ts);
    return double(ts.tv_sec) * 1000000.0 + double(ts.tv_nsec) / 1000.0;
}

//!
//! \brief Linux specific user feature define, used in MosUtilities::MosUserFeatureParsePath
//!        They can be unified with the win definitions, since they are identical.
//!
#define MOS_UF_SEPARATOR  "\\"
#define MOS_UFKEY_EXT     "UFKEY_EXTERNAL"
#define MOS_UFKEY_INT     "UFKEY_INTERNAL"

//!
//! \brief trace setting definition
//!
#define TRACE_SETTING_PATH             "/dev/shm/GFX_MEDIA_TRACE"
#define TRACE_SETTING_SIZE             sizeof(MtControlData)

//!
//! \brief Linux specific trace entry path and file description.
//!
const char *const MosUtilitiesSpecificNext::m_mosTracePath  = "/sys/kernel/debug/tracing/trace_marker_raw";
int32_t           MosUtilitiesSpecificNext::m_mosTraceFd    = -1;
uint64_t          MosUtilitiesSpecificNext::m_filterEnv     = 0;
uint32_t          MosUtilitiesSpecificNext::m_levelEnv      = 0;

MosMutex          MosUtilitiesSpecificNext::m_userSettingMutex;

//!
//! \brief trace event size definition
//!
#define TRACE_EVENT_MAX_SIZE           (3072)
#define TRACE_EVENT_HEADER_SIZE        (sizeof(uint32_t)*3)
#define TRACE_EVENT_MAX_DATA_SIZE      (TRACE_EVENT_MAX_SIZE - TRACE_EVENT_HEADER_SIZE - sizeof(uint16_t)) // Trace info data size section is in uint16_t

//!
//! \brief for int64_t/uint64_t format print warning
//!
#if __WORDSIZE == 64
#define __MOS64_PREFIX    "l"
#else
#define __MOS64_PREFIX    "ll"
#endif

#define MOSd64     __MOS64_PREFIX "d"
#define MOSu64     __MOS64_PREFIX "u"


//!
//! \brief mutex for mos utilities multi-threading protection
//!
MosMutex MosUtilities::m_mutexLock;
uint32_t MosUtilities::m_mosUtilInitCount = 0; // number count of mos utilities init

MediaUserSettingsMgr *MosUtilities::m_mediaUserFeatureSpecific  = nullptr;

MOS_STATUS MosUtilities::MosSecureStrcat(char  *strDestination, size_t numberOfElements, const char * const strSource)
{
    if ( (strDestination == nullptr) || (strSource == nullptr) )
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    if(strnlen(strDestination, numberOfElements) == numberOfElements) // Not null terminated
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    if((strlen(strDestination) + strlen(strSource)) >= numberOfElements) // checks space for null termination.
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    strcat(strDestination, strSource);
    return MOS_STATUS_SUCCESS;
}

char  *MosUtilities::MosSecureStrtok(
    char                *strToken,
    const char          *strDelimit,
    char                **contex)
{
    return strtok_r(strToken, strDelimit, contex);
}

MOS_STATUS MosUtilities::MosSecureStrcpy(char  *strDestination, size_t numberOfElements, const char * const strSource)
{
    if ( (strDestination == nullptr) || (strSource == nullptr) )
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    if ( numberOfElements <= strlen(strSource) ) // checks if there is space for null termination after copy.
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    strcpy(strDestination, strSource);

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosSecureStrncpy(char *strDestination, size_t destSz, const char* const strSource, size_t maxCount)
{
    if ( (strDestination == nullptr) || (strSource == nullptr) )
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    if ( destSz <= maxCount ) // checks if there is space for null termination after copy.
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    strncpy(strDestination, strSource, maxCount);

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosSecureMemcpy(void  *pDestination, size_t dstLength, PCVOID pSource, size_t srcLength)
{
    if ( (pDestination == nullptr) || (pSource == nullptr) )
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    if ( dstLength < srcLength )
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }
    if(pDestination != pSource)
    {
        memcpy(pDestination, pSource, srcLength);
    }

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosSecureFileOpen(
    FILE       **ppFile,
    const char *filename,
    const char *mode)
{
    PFILE fp;

    if ((ppFile == nullptr) || (filename == nullptr) || (mode == nullptr))
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    fp = fopen(filename, mode);

    if (fp == nullptr)
    {
        *ppFile = nullptr;
        return MOS_STATUS_FILE_OPEN_FAILED;
    }
    else
    {
        *ppFile = fp;
        return MOS_STATUS_SUCCESS;
    }
}

int32_t MosUtilities::MosSecureStringPrint(char  *buffer, size_t bufSize, size_t length, const char * const format, ...)
{
    int32_t iRet = -1;
    va_list var_args;

    if((buffer == nullptr) || (format == nullptr) || (bufSize < length))
    {
        return iRet;
    }

    va_start(var_args, format);

    iRet = vsnprintf(buffer, length, format, var_args);

    va_end(var_args);

    return iRet;
}

MOS_STATUS MosUtilities::MosSecureVStringPrint(char  *buffer, size_t bufSize, size_t length, const char * const format, va_list var_args)
{
    if((buffer == nullptr) || (format == nullptr) || (bufSize < length))
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    vsnprintf(buffer, length, format, var_args);

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosGetFileSize(
    HANDLE              hFile,
    uint32_t            *lpFileSizeLow,
    uint32_t            *lpFileSizeHigh)
{
    struct stat     Buf;
    MOS_UNUSED(lpFileSizeHigh);

    if((hFile == nullptr) || (lpFileSizeLow == nullptr))
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    if ( (fstat((intptr_t)hFile, &Buf)) < 0 )
    {
        *lpFileSizeLow = 0;
        return MOS_STATUS_INVALID_FILE_SIZE;
    }
    *lpFileSizeLow  = (uint32_t)Buf.st_size;

    //to-do, lpFileSizeHigh store high 32-bit of File size
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosCreateDirectory(
    char * const       lpPathName)
{
    uint32_t   mode;

    MOS_OS_CHK_NULL_RETURN(lpPathName);

    // Set read/write access right for usr/group.
    mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP;
    if (mkdir(lpPathName, mode) < 0 &&
        errno != EEXIST) // Directory already exists, don't return failure in this case.
    {
        MOS_OS_ASSERTMESSAGE("Failed to create the directory '%s'. Error = %s", lpPathName, strerror(errno));
        return MOS_STATUS_DIR_CREATE_FAILED;
    }

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosCreateFile(
    PHANDLE             pHandle,
    char * const        lpFileName,
    uint32_t            iOpenFlag)
{
    int32_t             iFileDescriptor;
    uint32_t            mode;

    if((lpFileName == nullptr) || (pHandle == nullptr))
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }
    //set read/write access right for usr/group, mMode only takes effect when
    //O_CREAT is set
    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
    if ( (iFileDescriptor = open(lpFileName, iOpenFlag, mode)) < 0 )
    {
        *pHandle = (HANDLE)((intptr_t) iFileDescriptor);
        return MOS_STATUS_INVALID_HANDLE;
    }

    *pHandle = (HANDLE)((intptr_t) iFileDescriptor);
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosReadFile(
    HANDLE  hFile,
    void    *lpBuffer,
    uint32_t bytesToRead,
    uint32_t *pBytesRead,
    void    *lpOverlapped)
{
    size_t  nNumBytesToRead;
    ssize_t nNumBytesRead;
    MOS_UNUSED(lpOverlapped);

    if((hFile == nullptr) || (lpBuffer == nullptr) || (pBytesRead == nullptr))
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    nNumBytesToRead   = (size_t)bytesToRead;
    nNumBytesRead     = 0;

    //To-do: process lpOverlapped

    if ((nNumBytesRead = read((intptr_t)hFile, lpBuffer, nNumBytesToRead)) < 0)
    {
        *pBytesRead = 0;
        return MOS_STATUS_FILE_READ_FAILED;
    }

    *pBytesRead = (uint32_t)nNumBytesRead;
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosWriteFile(
    HANDLE    hFile,
    void      *lpBuffer,
    uint32_t  bytesToWrite,
    uint32_t  *pbytesWritten,
    void      *lpOverlapped)
{
    size_t    nNumBytesToWrite;
    ssize_t   nNumBytesWritten;
    MOS_UNUSED(lpOverlapped);

    if((hFile == nullptr) || (lpBuffer == nullptr) || (pbytesWritten == nullptr))
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    nNumBytesToWrite = (size_t)bytesToWrite;
    nNumBytesWritten = 0;

    //To-do, process lpOverlapped

    if ((nNumBytesWritten = write((intptr_t)hFile, lpBuffer, nNumBytesToWrite)) < 0)
    {
        *pbytesWritten = 0;
        return MOS_STATUS_FILE_WRITE_FAILED;
    }

    *pbytesWritten = (uint32_t)nNumBytesWritten;
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosSetFilePointer(
    HANDLE        hFile,
    int32_t       lDistanceToMove,
    int32_t       *lpDistanceToMoveHigh,
    int32_t       dwMoveMethod)
{
    int32_t     iOffSet;
    int32_t     iCurPos;

    if(hFile == nullptr)
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    if (lpDistanceToMoveHigh == nullptr)
    {
        iOffSet = lDistanceToMove;
    }
    else
    {
        //to-do, let lpDistanceToMoveHigh and lDistanceToMove form a 64-bit iOffSet
        iOffSet = (int32_t)lDistanceToMove;
    }

    if ((iCurPos = lseek((intptr_t)hFile, iOffSet, dwMoveMethod)) < 0)
    {
        return MOS_STATUS_SET_FILE_POINTER_FAILED;
    }

    return MOS_STATUS_SUCCESS;
}

int32_t MosUtilities::MosCloseHandle(HANDLE hObject)
{
    int32_t iRet = false;

    if(hObject != nullptr)
    {
        close((intptr_t)hObject);
        iRet = true;
    }

    return iRet;
}

//library
MOS_STATUS MosUtilities::MosLoadLibrary(const char * const lpLibFileName, PHMODULE phModule)
{
    if (lpLibFileName == nullptr)
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    *phModule = dlopen((const char *)lpLibFileName, RTLD_LAZY);

    return ((*phModule != nullptr) ? MOS_STATUS_SUCCESS : MOS_STATUS_LOAD_LIBRARY_FAILED);
}

int32_t MosUtilities::MosFreeLibrary(HMODULE hLibModule)
{
    uint32_t iRet = 10;   // Initialize to some non-zero value

    if(hLibModule != nullptr)
    {
        iRet = dlclose(hLibModule);
    }
    return (iRet == 0) ? true : false;
}

void  *MosUtilities::MosGetProcAddress(HMODULE hModule, const char *lpProcName)
{
    void  *pSym = nullptr;

    if (hModule    == nullptr ||
        lpProcName == nullptr)
    {
        MOS_OS_ASSERTMESSAGE("Invalid parameter.");
    }
    else
    {
        pSym = dlsym(hModule, lpProcName);
    }

    return pSym;
}

int32_t MosUtilities::MosGetPid()
{
    return(getpid());
}

//Performace
int32_t MosUtilities::MosQueryPerformanceFrequency(uint64_t *pFrequency)
{
    struct timespec  Res;
    int32_t          iRet;

    if(pFrequency == nullptr)
    {
        return false;
    }

    if ( (iRet = clock_getres(CLOCK_MONOTONIC, &Res)) != 0 )
    {
        return false;
    }

    // resolution (precision) can't be in seconds for current machine and OS
    if (Res.tv_sec != 0)
    {
        return false;
    }
    *pFrequency = (uint64_t)((1000 * 1000 * 1000) / Res.tv_nsec);

    return true;
}

int32_t MosUtilities::MosQueryPerformanceCounter(uint64_t *pPerformanceCount)
{
    struct timespec     Res;
    struct timespec     t;
    int32_t             iRet;

    if(pPerformanceCount == nullptr)
    {
        return false;
    }
    if ( (iRet = clock_getres (CLOCK_MONOTONIC, &Res)) != 0 )
    {
        return false;
    }
    if (Res.tv_sec != 0)
    { // resolution (precision) can't be in seconds for current machine and OS
        return false;
    }
    if( (iRet = clock_gettime(CLOCK_MONOTONIC, &t)) != 0)
    {
        return false;
    }
    *pPerformanceCount = (uint64_t)((1000 * 1000 * 1000 * t.tv_sec + t.tv_nsec) / Res.tv_nsec);

    return true;
}

void MosUtilities::MosSleep(uint32_t mSec)
{
    usleep(1000 * mSec);
}

//User Feature
 MOS_UF_KEY* MosUtilitiesSpecificNext::UserFeatureFindKey(MOS_PUF_KEYLIST pKeyList, char * const pcKeyName)
{
    int32_t           iResult;
    MOS_PUF_KEYLIST   pTempNode;

    iResult = -1;

    for(pTempNode = pKeyList; pTempNode; pTempNode = pTempNode->pNext)
    {
        iResult = strcmp(pTempNode->pElem->pcKeyName, pcKeyName);
        if ( iResult == 0 )
        {
            return pTempNode->pElem;
        }
    }
    return nullptr; //not found
}

 int32_t MosUtilitiesSpecificNext::UserFeatureFindValue(MOS_UF_KEY UFKey, char * const pcValueName)
{
    int32_t iResult;
    int32_t i;

    iResult = -1;

    for ( i = 0; i < UFKey.valueNum; i++ )
    {
        iResult = strcmp(UFKey.pValueArray[i].pcValueName, pcValueName);
        if ( iResult == 0 )
        {
            return i;
        }
    }
    return NOT_FOUND;
}

MOS_STATUS MosUtilitiesSpecificNext::UserFeatureAdd(MOS_PUF_KEYLIST *pKeyList, MOS_UF_KEY *NewKey)
{
    MOS_UF_KEYNODE  *pNewNode;
    MOS_UF_KEYNODE  *pTempNode;
    MOS_UF_KEYNODE  *pStartNode;

    pNewNode   =  nullptr;
    pTempNode  =  nullptr;
    pStartNode =  *pKeyList;

    if ( NewKey == nullptr )
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    pNewNode = (MOS_UF_KEYNODE *)MOS_AllocAndZeroMemory(sizeof(MOS_UF_KEYNODE));
    if (pNewNode == nullptr)
    {
        return MOS_STATUS_NO_SPACE;
    }
    pNewNode->pElem = NewKey;

    if (*pKeyList == nullptr ) // the key list is empty
    {
        pNewNode->pNext = nullptr;
        (*pKeyList) = pNewNode;
    }
    else // the key list is not empty, append to the front
    {
        pTempNode = pStartNode->pNext;
        pStartNode->pNext = pNewNode;
        pNewNode->pNext = pTempNode;
    }
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilitiesSpecificNext::UserFeatureSet(MOS_PUF_KEYLIST *pKeyList, MOS_UF_KEY NewKey)
{
    int32_t       iPos;
    MOS_UF_VALUE  *pValueArray;
    MOS_UF_KEY    *Key;
    void          *ulValueBuf;

    iPos         = -1;
    pValueArray  = nullptr;

    if ( (Key = UserFeatureFindKey(*pKeyList, NewKey.pcKeyName)) == nullptr )
    {
        // can't find key in File
        return MOS_STATUS_UNKNOWN;
    }

    // Prepare the ValueBuff of the NewKey
    if ((ulValueBuf = MOS_AllocAndZeroMemory(NewKey.pValueArray[0].ulValueLen)) == nullptr)
    {
         return MOS_STATUS_NO_SPACE;
    }
    MosUtilities::MosAtomicIncrement(MosUtilities::m_mosMemAllocFakeCounter);  //ulValueBuf does not count it, because it is freed after the MEMNJA final report.
    MOS_OS_NORMALMESSAGE("ulValueBuf %p for key %s", ulValueBuf, NewKey.pValueArray[0].pcValueName);

    m_userSettingMutex.Lock();
    if ((iPos = UserFeatureFindValue(*Key, NewKey.pValueArray[0].pcValueName)) == NOT_FOUND)
    {
        //not found, add a new value to key struct.
        //reallocate memory for appending this value.
        iPos = MosUtilities::MosAtomicIncrement(&Key->valueNum);
        iPos = iPos - 1;
        if (iPos >= UF_CAPABILITY)
        {
            MOS_OS_ASSERTMESSAGE("user setting requires iPos (%d) < UF_CAPABILITY(64)", iPos);
            Key->valueNum = UF_CAPABILITY;
            MOS_SafeFreeMemory(ulValueBuf);
            m_userSettingMutex.Unlock();
            return MOS_STATUS_USER_FEATURE_KEY_READ_FAILED;
        }
        MosUtilities::MosSecureStrcpy(Key->pValueArray[iPos].pcValueName,
            MAX_USERFEATURE_LINE_LENGTH,
            NewKey.pValueArray[0].pcValueName);
    }
    else
    {
        //if found, the previous value buffer needs to be freed before reallocating
        MOS_FreeMemory(Key->pValueArray[iPos].ulValueBuf);
        MosUtilities::MosAtomicDecrement(MosUtilities::m_mosMemAllocFakeCounter);
        MOS_OS_NORMALMESSAGE("ulValueBuf %p for key %s", ulValueBuf, NewKey.pValueArray[0].pcValueName);
    }

    Key->pValueArray[iPos].ulValueLen  = NewKey.pValueArray[0].ulValueLen;
    Key->pValueArray[iPos].ulValueType = NewKey.pValueArray[0].ulValueType;
    Key->pValueArray[iPos].ulValueBuf  = ulValueBuf;

    MosUtilities::MosZeroMemory(Key->pValueArray[iPos].ulValueBuf, NewKey.pValueArray[0].ulValueLen);

    MosUtilities::MosSecureMemcpy(Key->pValueArray[iPos].ulValueBuf,
                     NewKey.pValueArray[0].ulValueLen,
                     NewKey.pValueArray[0].ulValueBuf,
                     NewKey.pValueArray[0].ulValueLen);

    m_userSettingMutex.Unlock();
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilitiesSpecificNext::UserFeatureQuery(MOS_PUF_KEYLIST pKeyList, MOS_UF_KEY *NewKey)
{
    int32_t       iPos;
    MOS_UF_VALUE  *pValueArray;
    MOS_UF_KEY    *Key;

    iPos         = -1;
    pValueArray  = nullptr;

    // can't find key in user feature
    if ( (Key = UserFeatureFindKey(pKeyList, NewKey->pcKeyName)) == nullptr )
    {
        return MOS_STATUS_UNKNOWN;
    }

    // can't find Value in the key
    if ( (iPos = UserFeatureFindValue(*Key, NewKey->pValueArray[0].pcValueName)) == NOT_FOUND)
    {
        return MOS_STATUS_UNKNOWN;
    }

    //get key content from user feature
    MosUtilities::MosSecureMemcpy(NewKey->pValueArray[0].ulValueBuf,
                     Key->pValueArray[iPos].ulValueLen,
                     Key->pValueArray[iPos].ulValueBuf,
                     Key->pValueArray[iPos].ulValueLen);

    NewKey->pValueArray[0].ulValueLen    =  Key->pValueArray[iPos].ulValueLen;
    NewKey->pValueArray[0].ulValueType   =  Key->pValueArray[iPos].ulValueType;

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS  MosUtilitiesSpecificNext::UserFeatureReadNextTokenFromFile(FILE *pFile, const char *szFormat, char  *szToken)
{
    size_t nTokenSize = 0;

    // Reads the next token from the given pFile.
    if (fscanf(pFile, szFormat, szToken) <= 0)
    {
        MOS_OS_VERBOSEMESSAGE("Failed reading the next token from the user feature file. This is probably because the token does not exist in the user feature file.");
        return MOS_STATUS_FILE_READ_FAILED;
    }

    // Converts to Unix-style line endings to prevent compatibility problems.
    nTokenSize = strnlen(szToken, MAX_USERFEATURE_LINE_LENGTH);
    if (szToken[nTokenSize-1] == '\r')
    {
        szToken[nTokenSize-1] = '\0';
    }

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS  MosUtilitiesSpecificNext::UserFeatureDumpFile(const char * const szFileName, MOS_PUF_KEYLIST* pKeyList)
{
    MOS_UF_KEY      *CurKey;
    MOS_UF_VALUE    *CurValue;
    char            szTmp[MAX_USERFEATURE_LINE_LENGTH];
    int32_t         iResult;
    size_t          nSize;
    int32_t         bFirst;
    int32_t         iCount;
    PFILE           File;
    int32_t         bEmpty;
    uint32_t        iCurId;
    MOS_STATUS      eStatus;
    char            *tmpChar; // Used in the 64-bit case to read uint64_t

    CurValue  =  nullptr;
    nSize     =  0;
    bFirst    =  1;    // 1 stand for "is the first key".
    iCount    =  0;
    File      =  nullptr;
    bEmpty    =  0;
    iCurId    =  0;
    eStatus  =  MOS_STATUS_SUCCESS;

    CurKey = (MOS_UF_KEY *)MOS_AllocAndZeroMemory(sizeof(MOS_UF_KEY));
    if (CurKey == nullptr)
    {
        return MOS_STATUS_NO_SPACE;
    }
    CurKey->valueNum        = 0;
    CurKey->pcKeyName[0]    = '\0';
    CurKey->pValueArray     = nullptr;

    if ( (File = fopen(szFileName, "r")) == nullptr)
    {
        MOS_FreeMemory(CurKey);
        return MOS_STATUS_USER_FEATURE_KEY_READ_FAILED;
    }
    while (feof(File) != EOF)
    {
        MosUtilities::MosZeroMemory(szTmp, MAX_USERFEATURE_LINE_LENGTH*sizeof(char ));
        if (MOS_FAILED(UserFeatureReadNextTokenFromFile(File, MAX_UF_LINE_STRING_FORMAT, szTmp)))
        {
            break;
        }

        // set szDumpData with extracted File content.
        iResult = strcmp(szTmp, UF_KEY_ID);
        if ( iResult == 0 )
        {
            // It is a new key starting!
            if (! bFirst )
            {
                // Add last key struct to contents when the key is not first.
                // otherwise, continue to load key struct data.
                CurKey->pValueArray   = CurValue;
                CurKey->valueNum      = iCount;
                if(UserFeatureAdd(pKeyList, CurKey) != MOS_STATUS_SUCCESS)
                {
                    // if the CurKey didn't be added in pKeyList, free it.
                    MOS_FreeMemory(CurKey);
                }
                CurKey = (MOS_UF_KEY *)MOS_AllocAndZeroMemory(sizeof(MOS_UF_KEY));
                if (CurKey == nullptr)
                {
                    eStatus = MOS_STATUS_NO_SPACE;
                    break;
                }
            } // if (! bFirst )

            if (fscanf(File, "%x\n", &iCurId) <= 0)
            {
                break;
            }

            CurKey->UFKey = (void *)(intptr_t)iCurId;

            MosUtilities::MosZeroMemory(szTmp, MAX_USERFEATURE_LINE_LENGTH * sizeof(char));
            if (MOS_FAILED(UserFeatureReadNextTokenFromFile(File, MAX_UF_LINE_STRING_FORMAT, szTmp)))
            {
                break;
            }

            MosUtilities::MosSecureStrcpy(CurKey->pcKeyName, MAX_USERFEATURE_LINE_LENGTH, szTmp);
            CurKey->valueNum = 0;

            // allocate capability length for valuearray.
            CurValue = (MOS_UF_VALUE *)MOS_AllocAndZeroMemory(sizeof(MOS_UF_VALUE) * UF_CAPABILITY);
            if (CurValue == nullptr)
            {
                eStatus = MOS_STATUS_NO_SPACE;
                break;
            }
            bFirst = 0;
            iCount = 0;  // next key's array number.
            bEmpty = 1;
        } // if ( iResult == 0 )
        else // not a key
        {
            // Is it a value starting?
            iResult = strcmp(szTmp, UF_VALUE_ID);
            if ( iResult == 0 )
            {
                if (MOS_FAILED(UserFeatureReadNextTokenFromFile(File, MAX_UF_LINE_STRING_FORMAT, szTmp)))
                {
                    break;
                }

                if (CurValue == nullptr)
                {
                    break;
                }

                // Out of bounds technically based on how much memory we allocated
                if (iCount < 0 || iCount >= UF_CAPABILITY)
                {
                    eStatus = MOS_STATUS_USER_FEATURE_KEY_READ_FAILED;
                    MOS_OS_ASSERTMESSAGE("user setting value icount %d, and it must meet  0 < icount < UF_CAPABILITY(64)", iCount);
                    break;
                }

                // Load value name;
                MosUtilities::MosSecureStrcpy(CurValue[iCount].pcValueName, MAX_USERFEATURE_LINE_LENGTH, szTmp);

                // Load value type
                if (MOS_FAILED(UserFeatureReadNextTokenFromFile(File, MAX_UF_LINE_STRING_FORMAT, szTmp)))
                {
                    break;
                }

                CurValue[iCount].ulValueType = atoi(szTmp);

                // Load value buffer.
                switch ( CurValue[iCount].ulValueType )
                {
                case UF_DWORD: // 32-bit
                    if (MOS_FAILED(UserFeatureReadNextTokenFromFile(File, MAX_UF_LINE_STRING_FORMAT, szTmp)))
                    {
                        break;
                    }

                    CurValue[iCount].ulValueLen = sizeof(uint32_t);
                    CurValue[iCount].ulValueBuf = MOS_AllocMemory(sizeof(uint32_t));
                    if(CurValue[iCount].ulValueBuf == nullptr)
                    {
                        eStatus = MOS_STATUS_NO_SPACE;
                        break;
                    }
                    *(uint32_t*)(CurValue[iCount].ulValueBuf) = atoi(szTmp);
                    break;
                case UF_QWORD: // 64-bit
                    if (MOS_FAILED(UserFeatureReadNextTokenFromFile(File, MAX_UF_LINE_STRING_FORMAT, szTmp)))
                    {
                        break;
                    }

                    CurValue[iCount].ulValueLen = sizeof(uint64_t);
                    CurValue[iCount].ulValueBuf = MOS_AllocMemory(sizeof(uint64_t));
                    if(CurValue[iCount].ulValueBuf == nullptr)
                    {
                        eStatus = MOS_STATUS_NO_SPACE;
                        break;
                    }
                    tmpChar = &szTmp[0];
                    *(uint64_t*)(CurValue[iCount].ulValueBuf) = strtoll(tmpChar,&tmpChar,0);
                    break;
                case UF_SZ:
                case UF_MULTI_SZ:
                    if (MOS_FAILED(UserFeatureReadNextTokenFromFile(File, MAX_UF_LINE_STRING_FORMAT, szTmp)))
                    {
                        break;
                    }

                    nSize = strlen(szTmp);
                    CurValue[iCount].ulValueLen = (nSize+1)*sizeof(char );
                    CurValue[iCount].ulValueBuf = MOS_AllocMemory(nSize+1);
                    if(CurValue[iCount].ulValueBuf == nullptr)
                    {
                        eStatus = MOS_STATUS_NO_SPACE;
                        break;
                    }
                    MosUtilities::MosZeroMemory(CurValue[iCount].ulValueBuf, nSize+1);
                    MosUtilities::MosSecureMemcpy(CurValue[iCount].ulValueBuf, nSize, szTmp, nSize);
                    break;
                default:
                    eStatus = MOS_STATUS_UNKNOWN;
                }
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    break;
                }

                iCount ++; // do the error checking near the top

            } // if ( iResult == 0 )
            else   // It is not a value starting, it's bad User Feature File.
            {
                int32_t iResult = strcmp(szTmp, "");
                if ( !iResult )
                {
                    continue;
                }
                else
                {
                    eStatus =  MOS_STATUS_INVALID_PARAMETER;
                    break;
                }
            } // else ( iResult == 0 )
        }
    } // while (feof(File) != EOF)

    if (eStatus == MOS_STATUS_SUCCESS)
    {
        if ( bEmpty && (strlen(CurKey->pcKeyName) > 0) &&
            (CurKey->valueNum == 0) )
        {
            CurKey->pValueArray = CurValue;
            CurKey->valueNum    = iCount;
            if(UserFeatureAdd(pKeyList, CurKey) != MOS_STATUS_SUCCESS)
            {
                // if the CurKey didn't be added in pKeyList, free it.
                for (uint32_t i = 0; i < iCount; i++)
                {
                    if (CurValue)
                    {
                        MOS_FreeMemory(CurValue[i].ulValueBuf);
                    }
                }
                MOS_FreeMemory(CurValue);
                MOS_FreeMemory(CurKey);
            }
        }
        else
        {
            for (uint32_t i = 0; i < iCount; i++)
            {
                if (CurValue)
                {
                    MOS_FreeMemory(CurValue[i].ulValueBuf);
                }
            }
            MOS_FreeMemory(CurValue);
            MOS_FreeMemory(CurKey);
        }
    }
    else
    {
        for (uint32_t i = 0; i < iCount; i++)
        {
            if (CurValue)
            {
                MOS_FreeMemory(CurValue[i].ulValueBuf);
            }
        }
        MOS_FreeMemory(CurValue);
        MOS_FreeMemory(CurKey);
    }
    fclose(File);
    return eStatus;
}

MOS_STATUS MosUtilitiesSpecificNext::UserFeatureDumpDataToFile(const char *szFileName, MOS_PUF_KEYLIST pKeyList)
{
    int32_t           iResult;
    PFILE             File;
    MOS_PUF_KEYLIST   pKeyTmp;
    int32_t           j;

   // For release version: writes to the file if it exists,
// skips if it does not exist
// For other version: always writes to the file

#if(_RELEASE)
    File = fopen(szFileName, "r");
    if (File == NULL)
    {
        return MOS_STATUS_SUCCESS;
    }
    else
    {
        fclose(File);
        File = fopen(szFileName, "w+");
    }
#else
    File = fopen(szFileName, "w+");
#endif

    if ( !File )
    {
        return MOS_STATUS_USER_FEATURE_KEY_WRITE_FAILED;
    }

    for (pKeyTmp = pKeyList; pKeyTmp; pKeyTmp = pKeyTmp->pNext)
    {
        fprintf(File, "%s\n", UF_KEY_ID);
        fprintf(File,  "\t0x%.8x\n", (uint32_t)(uintptr_t)pKeyTmp->pElem->UFKey);
        fprintf(File,  "\t%s\n", pKeyTmp->pElem->pcKeyName);
        for ( j = 0; j < pKeyTmp->pElem->valueNum; j ++ )
        {
            fprintf(File, "\t\t%s\n", UF_VALUE_ID);
            if ( strlen(pKeyTmp->pElem->pValueArray[j].pcValueName) > 0 )
            {
                fprintf(File, "\t\t\t%s\n",
                    pKeyTmp->pElem->pValueArray[j].pcValueName);
            }
            fprintf(File, "\t\t\t%d\n", pKeyTmp->pElem->pValueArray[j].ulValueType);
            if (pKeyTmp->pElem->pValueArray[j].ulValueBuf != nullptr)
            {
                switch (pKeyTmp->pElem->pValueArray[j].ulValueType)
                {
                case UF_SZ:
                    fprintf(File,  "\t\t\t%s\n",
                        (char *)(pKeyTmp->pElem->pValueArray[j].ulValueBuf));
                    break;
                case UF_DWORD:
                    fprintf(File, "\t\t\t%u\n",
                        *(uint32_t*)(pKeyTmp->pElem->pValueArray[j].ulValueBuf));
                    break;
                case UF_QWORD:
                    fprintf(File, "\t\t\t%" PRIu64"\n", *(uint64_t*)(pKeyTmp->pElem->pValueArray[j].ulValueBuf));
                    break;
                default:
                    fprintf(File, "\t\t\t%s\n",
                        (char *)(pKeyTmp->pElem->pValueArray[j].ulValueBuf));
                    break;
                } //switch (pKeyTmp->pElem->pValueArray[j].ulValueType)
            }
        } // for ( j = 0; j < pKeyTmp->pElem->valueNum; j ++ )
    } //for (pKeyTmp = pKeyList; pKeyTmp; pKeyTmp = pKeyTmp->pNext)
    fclose(File);

    return MOS_STATUS_SUCCESS;
}

void MosUtilitiesSpecificNext::UserFeatureFreeKeyList(MOS_PUF_KEYLIST pKeyList)
{
    MOS_PUF_KEYLIST     pKeyTmp;
    MOS_PUF_KEYLIST     pKeyTmpNext;
    uint32_t            i;

    pKeyTmp = pKeyList;
    while(pKeyTmp)
    {
        pKeyTmpNext = pKeyTmp->pNext;
        for(i=0;i<pKeyTmp->pElem->valueNum;i++)
        {
            MOS_FreeMemory(pKeyTmp->pElem->pValueArray[i].ulValueBuf);
        }
        MOS_FreeMemory(pKeyTmp->pElem->pValueArray);
        MOS_FreeMemory(pKeyTmp->pElem);
        MOS_FreeMemory(pKeyTmp);
        pKeyTmp = pKeyTmpNext;
    }
    return;
}

MOS_STATUS  MosUtilitiesSpecificNext::UserFeatureSetValue(
    char * const        strKey,
    const char * const  pcValueName,
    uint32_t            uiValueType,
    void                *pData,
    int32_t             nDataSize)
{
    MOS_UF_KEY          NewKey;
    MOS_UF_VALUE        NewValue;
    MOS_STATUS          eStatus;

    eStatus   = MOS_STATUS_UNKNOWN;

    if ((strKey == nullptr) || (pcValueName == nullptr) || (m_ufKeyList == nullptr))
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    MosUtilities::MosZeroMemory(NewValue.pcValueName, MAX_USERFEATURE_LINE_LENGTH);
    MosUtilities::MosSecureStrcpy(NewValue.pcValueName, MAX_USERFEATURE_LINE_LENGTH, pcValueName);
    NewValue.ulValueType    = uiValueType;
    if( NewValue.ulValueType == UF_DWORD)
    {
        NewValue.ulValueLen = sizeof(uint32_t);
    }
    else
    {
        NewValue.ulValueLen = nDataSize;
    }
    NewValue.ulValueBuf     = pData;

    MosUtilities::MosZeroMemory(NewKey.pcKeyName, MAX_USERFEATURE_LINE_LENGTH);
    MosUtilities::MosSecureStrcpy(NewKey.pcKeyName, MAX_USERFEATURE_LINE_LENGTH, strKey);
    NewKey.pValueArray = &NewValue;
    NewKey.valueNum    = 1;

    if ( ( eStatus = UserFeatureSet(&MosUtilitiesSpecificNext::m_ufKeyList, NewKey)) == MOS_STATUS_SUCCESS )
    {
        MosUtilities::MosUserFeatureNotifyChangeKeyValue(nullptr, false, nullptr, true);
    }

    return eStatus;
}

MOS_STATUS MosUtilitiesSpecificNext::UserFeatureQueryValue(
    char * const        strKey,
    const char * const  pcValueName,
    uint32_t            *uiValueType,
    void                *pData,
    int32_t             *nDataSize)
{
    MOS_UF_KEY          NewKey;
    MOS_UF_VALUE        NewValue;
    size_t              nKeyLen, nValueLen;
    MOS_STATUS          eStatus;
    MOS_PUF_KEYLIST     pKeyList;
    char                strTempKey[MAX_USERFEATURE_LINE_LENGTH];
    char                strTempValueName[MAX_USERFEATURE_LINE_LENGTH];

    eStatus   = MOS_STATUS_UNKNOWN;
    pKeyList   = MosUtilitiesSpecificNext::m_ufKeyList;

    if ( (strKey == nullptr) || (pcValueName == nullptr) || (pKeyList == nullptr))
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }
    MosUtilities::MosZeroMemory(NewValue.pcValueName, MAX_USERFEATURE_LINE_LENGTH);
    MosUtilities::MosSecureStrcpy(NewValue.pcValueName, MAX_USERFEATURE_LINE_LENGTH, pcValueName);
    NewValue.ulValueBuf     = pData;

    MosUtilities::MosZeroMemory(NewKey.pcKeyName, MAX_USERFEATURE_LINE_LENGTH);
    MosUtilities::MosSecureStrcpy(NewKey.pcKeyName, MAX_USERFEATURE_LINE_LENGTH, strKey);
    NewKey.pValueArray = &NewValue;
    NewKey.valueNum    = 1;

    if ( (eStatus = UserFeatureQuery(pKeyList, &NewKey)) == MOS_STATUS_SUCCESS )
    {
        if(uiValueType != nullptr)
        {
            *uiValueType = NewKey.pValueArray[0].ulValueType;
        }
        if (nDataSize != nullptr)
        {
            *nDataSize   = NewKey.pValueArray[0].ulValueLen;
        }
    }

    return eStatus;
}

MOS_STATUS MosUtilitiesSpecificNext::UserFeatureGetKeyIdbyName(const char  *pcKeyName, void **pUFKey)
{
    MOS_PUF_KEYLIST     pKeyList;
    int32_t             iResult;
    MOS_STATUS          eStatus;
    MOS_PUF_KEYLIST     pTempNode;

    pKeyList   = MosUtilitiesSpecificNext::m_ufKeyList;
    iResult    = -1;

    eStatus    = MOS_STATUS_INVALID_PARAMETER;

    for(pTempNode=pKeyList; pTempNode; pTempNode=pTempNode->pNext)
    {
        iResult = strcmp(pTempNode->pElem->pcKeyName, pcKeyName);
        if ( iResult == 0 )
        {
            *pUFKey = pTempNode->pElem->UFKey;
            eStatus = MOS_STATUS_SUCCESS;
            break;
        }
    }

    return eStatus;
}

MOS_STATUS MosUtilitiesSpecificNext::UserFeatureGetKeyNamebyId(void  *UFKey, char  *pcKeyName)
{
    MOS_PUF_KEYLIST     pKeyList;
    MOS_PUF_KEYLIST     pTempNode;
    MOS_STATUS          eStatus;

    pKeyList   = MosUtilitiesSpecificNext::m_ufKeyList;

    switch((uintptr_t)UFKey)
    {
    case UFKEY_INTERNAL:
        MosUtilities::MosSecureStrcpy(pcKeyName, MAX_USERFEATURE_LINE_LENGTH, USER_FEATURE_KEY_INTERNAL);
        eStatus = MOS_STATUS_SUCCESS;
        break;
    case UFKEY_EXTERNAL:
        MosUtilities::MosSecureStrcpy(pcKeyName, MAX_USERFEATURE_LINE_LENGTH, USER_FEATURE_KEY_EXTERNAL);
        eStatus = MOS_STATUS_SUCCESS;
        break;
    default:
        eStatus   = MOS_STATUS_UNKNOWN;

        for(pTempNode=pKeyList;pTempNode;pTempNode=pTempNode->pNext)
        {
            if(pTempNode->pElem->UFKey == UFKey)
            {
                MosUtilities::MosSecureStrcpy(pcKeyName, MAX_USERFEATURE_LINE_LENGTH, pTempNode->pElem->pcKeyName);
                eStatus = MOS_STATUS_SUCCESS;
                break;
            }
        }
        break;
    }

    return eStatus;

}

MOS_STATUS MosUtilitiesSpecificNext::MosUserFeatureOpenKeyFile(
    void       *UFKey,
    const char *lpSubKey,
    uint32_t   ulOptions,  // reserved
    uint32_t   samDesired,
    void       **phkResult)
{
    char           pcKeyName[MAX_USERFEATURE_LINE_LENGTH];
    MOS_STATUS     iRet;
    uintptr_t      h_key = (uintptr_t)UFKey;
    MOS_UNUSED(ulOptions);
    MOS_UNUSED(samDesired);

    if((h_key == 0) /*|| (lpSubKey == nullptr)*/ || (phkResult == nullptr))    //[SH]: subkey can be NULL???
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    MosUtilities::MosZeroMemory(pcKeyName, MAX_USERFEATURE_LINE_LENGTH*sizeof(char));
    switch(h_key)
    {
    case UFKEY_INTERNAL:
        MosUtilities::MosSecureStrcpy(pcKeyName, MAX_USERFEATURE_LINE_LENGTH, USER_FEATURE_KEY_INTERNAL);
        break;
    case UFKEY_EXTERNAL:
        MosUtilities::MosSecureStrcpy(pcKeyName, MAX_USERFEATURE_LINE_LENGTH, USER_FEATURE_KEY_EXTERNAL);
        break;
    default:
        break;
    }

    MosUtilities::MosSecureStrcat(pcKeyName, sizeof(pcKeyName), lpSubKey);
    iRet = UserFeatureGetKeyIdbyName(pcKeyName, phkResult);

    return iRet;
}

MOS_STATUS MosUtilitiesSpecificNext::MosUserFeatureGetValueFile(
    void       *UFKey,
    const char *lpSubKey,
    const char *lpValue,
    uint32_t   dwFlags,
    uint32_t   *pdwType,
    void       *pvData,
    uint32_t   *pcbData)
{
    char          pcKeyName[MAX_USERFEATURE_LINE_LENGTH];
    MOS_STATUS    eStatus;
    MOS_UNUSED(dwFlags);

    if(UFKey == nullptr)
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    eStatus = MOS_STATUS_UNKNOWN;
    MosUtilities::MosZeroMemory(pcKeyName, MAX_USERFEATURE_LINE_LENGTH * sizeof(char));
    if ( (eStatus = UserFeatureGetKeyNamebyId(UFKey,pcKeyName)) != MOS_STATUS_SUCCESS)
    {
        return eStatus;
    }

    if(lpSubKey != nullptr)
    {
        MosUtilities::MosSecureStrcat(pcKeyName, sizeof(pcKeyName), lpSubKey);
    }
    eStatus = UserFeatureQueryValue(pcKeyName,
                                  lpValue,
                                  (uint32_t*)pdwType,
                                  pvData,
                                  (int32_t*)pcbData);

    return eStatus;
}

MOS_STATUS MosUtilitiesSpecificNext::MosUserFeatureSetValueExFile(
    void            *UFKey,
    const char      *lpValueName,
    uint32_t        Reserved,
    uint32_t        dwType,
    uint8_t         *lpData,
    uint32_t        cbData)
{
    char    pcKeyName[MAX_USERFEATURE_LINE_LENGTH];
    MOS_STATUS  eStatus;
    MOS_UNUSED(Reserved);

    if (UFKey == nullptr)
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }
    MosUtilities::MosZeroMemory(pcKeyName, MAX_USERFEATURE_LINE_LENGTH*sizeof(char));
    if ((eStatus = UserFeatureGetKeyNamebyId(UFKey,pcKeyName)) != MOS_STATUS_SUCCESS)
    {
        return eStatus;
    }

    eStatus = UserFeatureSetValue(pcKeyName,lpValueName,dwType,lpData,cbData);

    return eStatus;
}

MOS_STATUS MosUtilities::MosOsUtilitiesInit(MediaUserSettingSharedPtr userSettingPtr)
{
    MOS_STATUS     eStatus = MOS_STATUS_SUCCESS;

    // lock mutex to avoid multi init in multi-threading env
    m_mutexLock.Lock();

#if (_DEBUG || _RELEASE_INTERNAL)
    // Get use user feature file from env, instead of default.
    FILE* fp = nullptr;
    static char* tmpFile = getenv("GFX_FEATURE_FILE");

    if (tmpFile != nullptr)
    {
      if ((fp = fopen(tmpFile, "r")) != nullptr)
      {
          MosUtilitiesSpecificNext::m_szUserFeatureFile = tmpFile;
          fclose(fp);
          MOS_OS_NORMALMESSAGE("using %s for USER_FEATURE_FILE", MosUtilitiesSpecificNext::m_szUserFeatureFile);
      }
      else
      {
          MOS_OS_ASSERTMESSAGE("Can't open %s for USER_FEATURE_FILE!!!", tmpFile);
          m_mutexLock.Unlock();
          return MOS_STATUS_FILE_NOT_FOUND;
      }
    }
#endif

    //The user setting is device based, no need to guard with the reference count.
    //It will be destroyed in media context termiation.
    eStatus = MosUserSetting::InitMosUserSetting(userSettingPtr);

    if (m_mosUtilInitCount == 0)
    {
        //Init MOS User Feature Key from mos desc table
        MosUtilitiesSpecificNext::UserFeatureDumpFile(MosUtilitiesSpecificNext::m_szUserFeatureFile, &MosUtilitiesSpecificNext::m_ufKeyList);
        MosDeclareUserFeature();

#if MOS_MESSAGES_ENABLED
        // Initialize MOS message params structure and HLT
        MosUtilDebug::MosMessageInit(userSettingPtr);
#endif // MOS_MESSAGES_ENABLED
        if (m_mosMemAllocCounter &&
            m_mosMemAllocCounterGfx &&
            m_mosMemAllocFakeCounter)
        {
            *m_mosMemAllocCounter     = 0;
            *m_mosMemAllocFakeCounter = 0;
            *m_mosMemAllocCounterGfx  = 0;
        }
        else
        {
            MOS_OS_ASSERTMESSAGE("MemNinja count pointers are nullptr");
        }

        MosTraceEventInit();
    }
    m_mosUtilInitCount++;

    m_mutexLock.Unlock();
    return eStatus;
}

MOS_STATUS MosUtilities::MosOsUtilitiesClose(MediaUserSettingSharedPtr userSettingPtr)
{
    int32_t                             memoryCounter = 0;
    MOS_STATUS                          eStatus = MOS_STATUS_SUCCESS;

    // lock mutex to avoid multi close in multi-threading env
    m_mutexLock.Lock();
    m_mosUtilInitCount--;
    if (m_mosUtilInitCount == 0)
    {
        MosTraceEventClose();
        if (m_mosMemAllocCounter &&
            m_mosMemAllocCounterGfx &&
            m_mosMemAllocFakeCounter)
        {
            *m_mosMemAllocCounter -= *m_mosMemAllocFakeCounter;
            memoryCounter = *m_mosMemAllocCounter + *m_mosMemAllocCounterGfx;
            m_mosMemAllocCounterNoUserFeature    = *m_mosMemAllocCounter;
            m_mosMemAllocCounterNoUserFeatureGfx = *m_mosMemAllocCounterGfx;
            MOS_OS_VERBOSEMESSAGE("MemNinja leak detection end");
            ReportUserSetting(
                userSettingPtr,
                __MEDIA_USER_FEATURE_VALUE_MEMNINJA_COUNTER,
                memoryCounter,
                MediaUserSetting::Group::Device);
        }
        MosDestroyUserFeature();

#if (_DEBUG || _RELEASE_INTERNAL)
        // MOS maintains a reference counter,
        // so if there still is another active lib instance, logs would still be printed.
        MosUtilDebug::MosMessageClose();
#endif
        MosUtilitiesSpecificNext::UserFeatureDumpDataToFile(MosUtilitiesSpecificNext::m_szUserFeatureFile, MosUtilitiesSpecificNext::m_ufKeyList);
        MosUtilitiesSpecificNext::UserFeatureFreeKeyList(MosUtilitiesSpecificNext::m_ufKeyList);
        MosUtilitiesSpecificNext::m_ufKeyList = nullptr;
    }
    m_mutexLock.Unlock();
    return eStatus;
}

MOS_STATUS MosUtilities::MosUserFeatureOpenKey(
    void       *ufKey,
    const char *lpSubKey,
    uint32_t   ulOptions,
    uint32_t   samDesired,
    void       **phkResult,
    MOS_USER_FEATURE_KEY_PATH_INFO  *ufInfo)
{
    MOS_UNUSED(ufInfo);

    return MosUtilitiesSpecificNext::MosUserFeatureOpenKeyFile(ufKey, lpSubKey, ulOptions, samDesired, phkResult);
}

MOS_STATUS MosUtilities::MosUserFeatureCloseKey(void  *ufKey)
{
    MOS_UNUSED(ufKey);
    //always return success, because we actually dong't have a key opened.
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosUserFeatureGetValue(
    void       *UFKey,
    const char *lpSubKey,
    const char *lpValue,
    uint32_t   dwFlags,
    uint32_t   *pdwType,
    void       *pvData,
    uint32_t   *pcbData)
{
    return MosUtilitiesSpecificNext::MosUserFeatureGetValueFile(UFKey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData);

}

MOS_STATUS MosUtilities::MosUserFeatureQueryValueEx(
    void            *UFKey,
    char            *lpValueName,
    uint32_t        *lpReserved,
    uint32_t        *lpType,
    char            *lpData,
    uint32_t        *lpcbData)
{
    MOS_UNUSED(lpReserved);
    return MosUserFeatureGetValue(UFKey, "", lpValueName, 0, lpType, lpData, lpcbData);
}

MOS_STATUS MosUtilities::MosUserFeatureSetValueEx(
    void            *UFKey,
    const char      *lpValueName,
    uint32_t        Reserved,
    uint32_t        dwType,
    uint8_t         *lpData,
    uint32_t        cbData)
{
    if (dwType == UF_SZ || dwType == UF_MULTI_SZ)
    {
        if (lpData == nullptr || strlen((const char*)lpData) == 0)
        {
            MOS_OS_NORMALMESSAGE("NULL string, skip to report");
            return MOS_STATUS_SUCCESS;
        }
    }

    return MosUtilitiesSpecificNext::MosUserFeatureSetValueExFile(UFKey, lpValueName, Reserved, dwType, lpData, cbData);
}

MOS_STATUS MosUtilities::MosUserFeatureNotifyChangeKeyValue(
    void                *UFKey,
    int32_t             bWatchSubtree,
    HANDLE              hEvent,
    int32_t             fAsynchronous)
{
    key_t          key;
    int32_t        semid;
    struct sembuf  operation[1] ;

    key = ftok(MosUtilitiesSpecificNext::m_szUserFeatureFile, 1);
    semid = semget(key,1,0);
    //change semaphore
    operation[0].sem_op  = 1;
    operation[0].sem_num = 0;
    operation[0].sem_flg = SEM_UNDO;
    semop(semid, operation, 1);

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosInitializeReg(RegBufferMap &regBufferMap)
{
    MOS_STATUS status = MOS_STATUS_SUCCESS;

    std::ifstream regStream;
    try
    {
        regStream.open(USER_FEATURE_FILE_NEXT);
        if (regStream.good())
        {
            std::string id       = "";

            while(!regStream.eof())
            {
                std::string line = "";
                std::getline(regStream, line);
                auto endIndex = line.find("\r");
                if(endIndex != std::string::npos)
                {
                    line = line.substr(0, endIndex);
                }
                if (std::string::npos != line.find(USER_SETTING_CONFIG_PATH))
                {
                    id = USER_SETTING_CONFIG_PATH;
                }
                else if (std::string::npos != line.find(USER_SETTING_REPORT_PATH))
                {
                    id = USER_SETTING_REPORT_PATH;
                }
                else if (line.find("]") != std::string::npos)
                {
                    auto mkPos = line.find_last_of("]");
                    id = line.substr(0, mkPos+1);
                }
                else
                {
                    if (id == USER_SETTING_REPORT_PATH)
                    {
                        continue;
                    }

                    std::size_t pos = line.find("=");
                    if (std::string::npos != pos && !id.empty())
                    {
                        std::string name = line.substr(0,pos);
                        std::string value = line.substr(pos+1);
                        auto        &keys  = regBufferMap[id];
                        keys[name]       = value;
                    }
                }
            }

        }
    }
    catch(const std::exception &e)
    {
        status = MOS_STATUS_FILE_OPEN_FAILED;
    }

    regStream.close();

    return status;
}

MOS_STATUS MosUtilities::MosUninitializeReg(RegBufferMap &regBufferMap)
{
    MOS_STATUS status = MOS_STATUS_SUCCESS;
    if (regBufferMap.size() == 0)
    {
        return MOS_STATUS_SUCCESS;
    }

    auto iter = regBufferMap.find(USER_SETTING_REPORT_PATH);
    // No need to write the user setting files if no session [report].
    // The session [config] is read only.
    if(iter == regBufferMap.end() || iter->second.size() == 0)
    {
        MOS_OS_NORMALMESSAGE("no report session");
        return MOS_STATUS_SUCCESS;
    }

    std::ofstream regStream;
    try
    {
// For release version: writes to the file if it exists,
// skips if it does not exist
// For other version: always writes to the file
#if(_RELEASE)
        std::ifstream regFile(USER_FEATURE_FILE_NEXT);
        if (regFile.good())
        {
            regFile.close();
            regStream.open(USER_FEATURE_FILE_NEXT, std::ios::out | std::ios::trunc);
        }
        else
        {
            regFile.close();
            return status;
        }
#else
        regStream.open(USER_FEATURE_FILE_NEXT, std::ios::out | std::ios::trunc);
#endif
        if (regStream.good())
        {
            for(auto pair: regBufferMap)
            {
                regStream << pair.first << "\n";

                auto &keys = regBufferMap[pair.first];
                for (auto key: keys)
                {
                    auto name = key.first;
                    regStream << key.first << "=" << key.second << "\n";
                }

                keys.clear();
                regStream << std::endl;
            }
            regBufferMap.clear();
            regStream.flush();
        }
    }
    catch(const std::exception &e)
    {
        status = MOS_STATUS_FILE_WRITE_FAILED;
    }

    regStream.close();
    return status;
}

MOS_STATUS MosUtilities::MosCreateRegKey(
    UFKEY_NEXT keyHandle,
    const std::string &subKey,
    uint32_t samDesired,
    PUFKEY_NEXT key,
    RegBufferMap &regBufferMap)
{
    MOS_UNUSED(keyHandle);
    MOS_UNUSED(samDesired);
    MOS_OS_CHK_NULL_RETURN(key);
    auto ret = regBufferMap.find(subKey);

    if (ret == regBufferMap.end())
    {
        regBufferMap[subKey] = {};
    }

    *key = subKey;
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosOpenRegKey(
    UFKEY_NEXT keyHandle,
    const std::string &subKey,
    uint32_t samDesired,
    PUFKEY_NEXT key,
    RegBufferMap &regBufferMap)
{
    std::string tempSubKey = subKey;

    if (subKey.find_first_of("\\") != std::string::npos)
    {
        tempSubKey = subKey.substr(1);
    }

    if (tempSubKey.find_first_of("[") == std::string::npos)
    {
        tempSubKey = "[" + tempSubKey + "]";
    }
    return MosCreateRegKey(keyHandle, tempSubKey, samDesired, key, regBufferMap);
}

MOS_STATUS MosUtilities::MosCloseRegKey(
    UFKEY_NEXT keyHandle)
{
    MOS_UNUSED(keyHandle);
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosReadEnvVariable(
    const std::string           &envName,
    MOS_USER_FEATURE_VALUE_TYPE defaultType,
    MediaUserSetting::Value     &data)
{
    char *retVal = getenv(envName.c_str());
    if (retVal != nullptr)
    {
        std::string strData = retVal;
        auto status = StrToMediaUserSettingValue(strData, defaultType, data);
        return status;
    }

    return MOS_STATUS_INVALID_PARAMETER;
}

bool MosUtilities::MosEnvVariableEqual(
    const std::string            envName,
    const std::string            targetVal)
{
    char *retVal = getenv(envName.c_str());
    bool res = false;
    if (retVal != nullptr)
    {
        res = strcmp(retVal, targetVal.c_str()) ? false : true;
    }
    return res;
}

MOS_STATUS MosUtilities::MosGetRegValue(
    UFKEY_NEXT keyHandle,
    const std::string &valueName,
    MOS_USER_FEATURE_VALUE_TYPE defaultType,
    MediaUserSetting::Value &data,
    RegBufferMap &regBufferMap)
{
    MOS_STATUS status = MOS_STATUS_SUCCESS;

    if (regBufferMap.end() == regBufferMap.find(keyHandle))
    {
        return MOS_STATUS_USER_FEATURE_KEY_OPEN_FAILED;
    }

    try
    {
        auto keys = regBufferMap[keyHandle];
        auto it = keys.find(valueName);
        if (it == keys.end())
        {
            return MOS_STATUS_USER_FEATURE_KEY_OPEN_FAILED;
        }
        status = MosUtilities::StrToMediaUserSettingValue(it->second, defaultType, data);
    }
    catch(const std::exception &e)
    {
        status = MOS_STATUS_INVALID_PARAMETER;
    }

    return status;
}

MOS_STATUS MosUtilities::MosPrintCPUAllocateMemory(int32_t event_id, int32_t level, int32_t param_id_1, int64_t value_1, int32_t param_id_2, int64_t value_2,
    const char *funName, const char *fileName, int32_t line)
{
#if (_RELEASE_INTERNAL)
    if (MOS_IS_MEMORY_FOOT_PRINT_ENABLED())
    {
        MosAtomicIncrement(m_mosMemAllocIndex);
        MT_LOG3(event_id, level, param_id_1, value_1, MT_MEMORY_INDEX, *MosUtilities::m_mosMemAllocIndex, param_id_2, value_2);
    }

#endif
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosPrintCPUDestroyMemory(int32_t event_id, int32_t level, int32_t param_id_1, int64_t value_1, 
    const char *funName, const char *fileName, int32_t line)
{
#if (_RELEASE_INTERNAL)
    if (MOS_IS_MEMORY_FOOT_PRINT_ENABLED())
    {
        MosAtomicDecrement(m_mosMemAllocIndex);
        MT_LOG2(event_id, level, param_id_1, value_1, MT_MEMORY_INDEX, *MosUtilities::m_mosMemAllocIndex);
    }
#endif
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosSetRegValue(
    UFKEY_NEXT keyHandle,
    const std::string &valueName,
    const MediaUserSetting::Value &data,
    RegBufferMap &regBufferMap)
{
    if (regBufferMap.end() == regBufferMap.find(keyHandle))
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }

    MOS_STATUS status = MOS_STATUS_SUCCESS;
    try
    {
        auto &keys = regBufferMap[keyHandle];

        keys[valueName] = data.ConstString();
    }
    catch(const std::exception &e)
    {
        status = MOS_STATUS_INVALID_PARAMETER;
    }

    return status;
}

HANDLE MosUtilities::MosCreateEventEx(
    void                *lpEventAttributes,
    char                *lpName,
    uint32_t            dwFlags)
{
    int32_t     semid;
    key_t       key;
    union semun
    {
        int32_t val;
        struct semid_ds *Buf;
        unsigned short *array;
    } semctl_arg;

    semid = 0;

    //Generate a unique key, U can also supply a value instead
    key = ftok(MosUtilitiesSpecificNext::m_szUserFeatureFile, 1);
    semid = semget(key,  1, 0666 | IPC_CREAT );
    semctl_arg.val = 0; //Setting semval to 0
    semctl(semid, 0, SETVAL, semctl_arg);

    HANDLE ret = reinterpret_cast<HANDLE>(semid);

    return ret;
}

int32_t MosUtilities::MosUserFeatureWaitForSingleObject(
    PTP_WAIT*           phNewWaitObject,
    HANDLE              hObject,
    void                *Callback,
    void                *Context)
{
    int32_t                  iRet;
    int32_t                  semid;
    struct sembuf            operation[1];
    pid_t                    pid;
    MOS_UserFeatureCallback  pCallback;
    LARGE_INTEGER            largeInteger;

    pCallback = (MOS_UserFeatureCallback)Callback;

    iRet  = 0;

    largeInteger.QuadPart = (int64_t)hObject;

    semid = largeInteger.u.LowPart;

    if ((pid=fork()) == -1)
    {
        printf("error\n");
    }
    else if(pid == 0)
    {
        while(1)
        {
            operation[0].sem_op = -1;
            operation[0].sem_num = 0;
            //now waiting
            semop(semid, operation, 1);
            pCallback(Context, 0);
        }
        exit(0);
    }
    else
    {
        iRet = pid;
    }

    *phNewWaitObject = reinterpret_cast<PTP_WAIT>(iRet);

    return (iRet != 0);
}

int32_t MosUtilities::MosUnregisterWaitEx(PTP_WAIT hWaitHandle)
{
    int32_t       iPid;
    LARGE_INTEGER largeInteger;

    largeInteger.QuadPart = (int64_t)hWaitHandle;

    iPid = largeInteger.u.LowPart;
    kill(iPid, SIGKILL);
    return true;
}

#if (_DEBUG || _RELEASE_INTERNAL)
MOS_STATUS MosUtilities::MosGetApoMosEnabledUserFeatureFile()
{
    // Get use user feature file from env, instead of default.
    FILE *       fp      = nullptr;
    static char *tmpFile = getenv("GFX_FEATURE_FILE");

    if (tmpFile != nullptr)
    {
        if ((fp = fopen(tmpFile, "r")) != nullptr)
        {
            if (MosUtilitiesSpecificNext::m_szUserFeatureFile != tmpFile)
                MosUtilitiesSpecificNext::m_szUserFeatureFile = tmpFile;
            fclose(fp);
            MOS_OS_NORMALMESSAGE("using %s for USER_FEATURE_FILE", MosUtilitiesSpecificNext::m_szUserFeatureFile);
        }
        else
        {
            MOS_OS_ASSERTMESSAGE("Can't open %s for USER_FEATURE_FILE!!!", tmpFile);
            return MOS_STATUS_FILE_NOT_FOUND;
        }
    }
    return MOS_STATUS_SUCCESS;
}
#endif

MOS_STATUS MosUtilities::MosReadMediaSoloEnabledUserFeature(bool &mediasoloEnabled)
{
    MOS_STATUS eStatus  = MOS_STATUS_SUCCESS;

#if MOS_MEDIASOLO_SUPPORTED

    void *     UFKey    = nullptr;
    uint32_t   dwUFSize = 0;
    uint32_t   data     = 0;

#if (_DEBUG || _RELEASE_INTERNAL)
    eStatus = MosGetApoMosEnabledUserFeatureFile();
    if (eStatus != MOS_STATUS_SUCCESS)
    {
        MOS_OS_NORMALMESSAGE("Failed to get user feature file, error status %d.", eStatus);
        return eStatus;
    }
#endif

    eStatus = MosUserFeatureOpen(
        MOS_USER_FEATURE_TYPE_USER,
        __MEDIA_USER_FEATURE_SUBKEY_INTERNAL,
        KEY_READ,
        &UFKey,
        nullptr);

    if (eStatus != MOS_STATUS_SUCCESS)
    {
        MOS_OS_NORMALMESSAGE("Failed to open user feature key , error status %d.", eStatus);
        return eStatus;
    }

    eStatus = MosUserFeatureGetValue(
        UFKey,
        nullptr,
        __MEDIA_USER_FEATURE_VALUE_MEDIASOLO_ENABLE,
        RRF_RT_UF_DWORD,
        nullptr,
        &data,
        &dwUFSize);

    if (eStatus == MOS_STATUS_SUCCESS && data > 0)
    {
        mediasoloEnabled = true;
    }
    MosUserFeatureCloseKey(UFKey); 

#endif
    return eStatus;
}

MOS_STATUS MosUtilities::MosReadApoDdiEnabledUserFeature(uint32_t &userfeatureValue, char *path, MediaUserSettingSharedPtr userSettingPtr)
{
    MOS_STATUS eStatus  = MOS_STATUS_SUCCESS;
    MOS_UNUSED(path);

    uint32_t enableApoDdi = 0;
    eStatus = ReadUserSetting(
        userSettingPtr,
        enableApoDdi,
        "ApoDdiEnable",
        MediaUserSetting::Group::Device);

    if (eStatus != MOS_STATUS_SUCCESS)
    {
        // It could be there is no this use feature key.
        MOS_OS_NORMALMESSAGE("Failed to read ApoDdiEnable user feature key value, error status %d", eStatus);
    }
    userfeatureValue = enableApoDdi ? true : false;
    return eStatus;
}

MOS_STATUS MosUtilities::MosReadApoMosEnabledUserFeature(uint32_t &userfeatureValue, char *path, MediaUserSettingSharedPtr userSettingPtr)
{
    MOS_STATUS eStatus  = MOS_STATUS_SUCCESS;
    MOS_USER_FEATURE_VALUE_DATA userFeatureData     = {};
    MOS_UNUSED(path);

    //If apo mos enabled, to check if media solo is enabled. Disable apo mos if media solo is enabled.
#if MOS_MEDIASOLO_SUPPORTED
    eStatus = MosUtilities::MosUserFeatureReadValueID(
            nullptr,
            __MEDIA_USER_FEATURE_VALUE_MEDIASOLO_ENABLE_ID,
            &userFeatureData,
            (MOS_CONTEXT_HANDLE) nullptr);

    //If media solo is enabled, disable apogeios.
    if (eStatus == MOS_STATUS_SUCCESS && userFeatureData.i32Data != 0)
    {
        // This error case can be hit if the user feature key does not exist.
        MOS_OS_NORMALMESSAGE("Solo is enabled, disable apo mos");
        userfeatureValue = 0;
        return MOS_STATUS_SUCCESS;
    }
#endif

    uint32_t enableApoMos = 0;
    eStatus = ReadUserSetting(
        userSettingPtr,
        enableApoMos,
        "ApoMosEnable",
        MediaUserSetting::Group::Device);

    if (eStatus != MOS_STATUS_SUCCESS)
    {
        // It could be there is no this use feature key.
        MOS_OS_NORMALMESSAGE("Failed to read ApoMosEnable  user feature key value, error status %d", eStatus);
    }
    userfeatureValue = enableApoMos ? true : false;
    return eStatus;
}

MOS_STATUS MosUtilities::MosUserFeatureParsePath(
    PMOS_USER_FEATURE_INTERFACE     pOsUserFeatureInterface,
    char * const                    pInputPath,
    PMOS_USER_FEATURE_TYPE          pUserFeatureType,
    char                            **ppSubPath)
{
    char                            *pValue;
    MOS_USER_FEATURE_TYPE           UserFeatureType;
    size_t                          uUFKeyLen;
    size_t                          uHKeyLen;
    size_t                          uValLen;
    size_t                          uSepLen;
    MOS_UNUSED(pOsUserFeatureInterface);

    //-------------------------------------------
    // the UserFeature interface is not currently an actual interface, just a collection
    // of functions, so pOsUserFeatureInterface will always be nullptr until this changes
    //MOS_OS_ASSERT(pOsUserFeatureInterface);
    MOS_OS_ASSERT(pInputPath);
    MOS_OS_ASSERT(strlen(pInputPath) > 0);
    MOS_OS_ASSERT(pUserFeatureType);
    MOS_OS_ASSERT(ppSubPath);
    //-------------------------------------------

    pValue = nullptr;

    pValue = strstr(pInputPath, MOS_UF_SEPARATOR);

    if (!pValue)
    {
        MOS_OS_ASSERTMESSAGE("Invalid user feature key %s.", pInputPath);
        return MOS_STATUS_INVALID_PARAMETER;
    }

    uUFKeyLen   = strlen(pInputPath);
    uValLen     = strlen(pValue);
    uSepLen     = strlen(MOS_UF_SEPARATOR);
    uHKeyLen    = uUFKeyLen - uValLen;

    if (uHKeyLen == 0)
    {
        MOS_OS_ASSERTMESSAGE("Invalid user feature key %s. Path separator in the begining.", pInputPath);
        return MOS_STATUS_INVALID_PARAMETER;
    }

    if (uValLen <= uSepLen)
    {
        MOS_OS_ASSERTMESSAGE("Invalid user feature key %s. No value after path separator.", pInputPath);
        return MOS_STATUS_INVALID_PARAMETER;
    }

    if ((uHKeyLen == strlen(MOS_UFKEY_EXT)) &&
        (strncmp(pInputPath, MOS_UFKEY_EXT, uHKeyLen) == 0))
    {
        UserFeatureType = MOS_USER_FEATURE_TYPE_SYSTEM;
    }
    else if ((uHKeyLen == strlen(MOS_UFKEY_INT)) &&
        (strncmp(pInputPath, MOS_UFKEY_INT, uHKeyLen) == 0))
    {
        UserFeatureType = MOS_USER_FEATURE_TYPE_USER;
    }
    else
    {
        MOS_OS_ASSERTMESSAGE("Invalid user feature key %s. Expected %s or %s.", pInputPath, MOS_UFKEY_EXT, MOS_UFKEY_INT);
        return MOS_STATUS_INVALID_PARAMETER;
    }

    pValue             = pValue + uSepLen;

    *pUserFeatureType  = UserFeatureType;
    *ppSubPath         = pValue;

    return MOS_STATUS_SUCCESS;
}

uint32_t MosUtilities::MosGetLogicalCoreNumber()
{
    return sysconf(_SC_NPROCESSORS_CONF);
}

MOS_THREADHANDLE MosUtilities::MosCreateThread(
    void                        *ThreadFunction,
    void                        *ThreadData)
{
    MOS_THREADHANDLE Thread;

    if (0 != pthread_create(&Thread, nullptr, (void *(*)(void *))ThreadFunction, ThreadData))
    {
        Thread = 0;
        MOS_OS_ASSERTMESSAGE("Create thread failed.");
    }

    return Thread;
}

uint32_t MosUtilities::MosGetThreadId(
    MOS_THREADHANDLE            hThread)
{
    MOS_UNUSED(hThread);
    return 0;
}

uint32_t MosUtilities::MosGetCurrentThreadId()
{
    return (uint32_t)pthread_self();
}

MOS_STATUS MosUtilities::MosWaitThread(
    MOS_THREADHANDLE            hThread)
{
    MOS_STATUS                  eStatus = MOS_STATUS_SUCCESS;

    if (hThread == 0)
    {
        MOS_OS_ASSERTMESSAGE("MOS wait thread failed, invalid thread handle.");
        eStatus = MOS_STATUS_INVALID_PARAMETER;
    }
    else if (0 != pthread_join(hThread, nullptr))
    {
        MOS_OS_ASSERTMESSAGE("Failed to join thread.");
        eStatus = MOS_STATUS_UNKNOWN;
    }

    return eStatus;
}

PMOS_MUTEX MosUtilities::MosCreateMutex(uint32_t spinCount)
{
    PMOS_MUTEX pMutex;
    MOS_UNUSED(spinCount);
    pMutex = (PMOS_MUTEX)MOS_AllocMemory(sizeof(*pMutex));
    if (pMutex != nullptr)
    {
        if (pthread_mutex_init(pMutex, nullptr))
        {
            MOS_FreeMemory(pMutex);
            pMutex = nullptr;
        }
    }

    return pMutex;
}

MOS_STATUS MosUtilities::MosDestroyMutex(PMOS_MUTEX pMutex)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    if (pMutex)
    {
        if (pthread_mutex_destroy(pMutex))
        {
            eStatus = MOS_STATUS_UNKNOWN;
        }
        MOS_FreeMemory(pMutex);
    }

    return eStatus;
}

MOS_STATUS MosUtilities::MosLockMutex(PMOS_MUTEX pMutex)
{
    MOS_OS_CHK_NULL_RETURN(pMutex);

    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    if (pthread_mutex_lock(pMutex))
    {
        eStatus = MOS_STATUS_UNKNOWN;
    }

    return eStatus;
}

MOS_STATUS MosUtilities::MosUnlockMutex(PMOS_MUTEX pMutex)
{
    MOS_OS_CHK_NULL_RETURN(pMutex);

    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    if (pthread_mutex_unlock(pMutex))
    {
        eStatus = MOS_STATUS_UNKNOWN;
    }

    return eStatus;
}

PMOS_SEMAPHORE MosUtilities::MosCreateSemaphore(
    uint32_t            uiInitialCount,
    uint32_t            uiMaximumCount)
{
    PMOS_SEMAPHORE pSemaphore = nullptr;
    MOS_UNUSED(uiMaximumCount);

    pSemaphore = (PMOS_SEMAPHORE)MOS_AllocMemory(sizeof(*pSemaphore));
    if (!pSemaphore)
        return nullptr;
    if (sem_init(pSemaphore, 0, uiInitialCount))
    {
        MOS_SafeFreeMemory(pSemaphore);
        pSemaphore = nullptr;
    }

    return pSemaphore;
}

MOS_STATUS MosUtilities::MosDestroySemaphore(
    PMOS_SEMAPHORE              pSemaphore)
{
    MOS_SafeFreeMemory(pSemaphore);

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MosUtilities::MosWaitSemaphore(
    PMOS_SEMAPHORE              pSemaphore,
    uint32_t                    uiMilliseconds)
{
    MOS_STATUS                  eStatus = MOS_STATUS_SUCCESS;

    if (uiMilliseconds == INFINITE)
    {
        if (sem_wait(pSemaphore))
        {
            eStatus = MOS_STATUS_UNKNOWN;
        }
    }
    else
    {
        struct timespec time = {
            (int32_t)uiMilliseconds / 1000000,
            ((int32_t)uiMilliseconds % 1000000) * 1000};

        if (sem_timedwait(pSemaphore, &time))
        {
            eStatus = MOS_STATUS_UNKNOWN;
        }
    }

    return eStatus;
}

MOS_STATUS MosUtilities::MosPostSemaphore(
    PMOS_SEMAPHORE              pSemaphore,
    uint32_t                    uiPostCount)
{
    MOS_STATUS                  eStatus = MOS_STATUS_SUCCESS;

    if (uiPostCount > 0)
    {
        while (uiPostCount--)
        {
            if (sem_post(pSemaphore))
            {
                eStatus = MOS_STATUS_UNKNOWN;
                break;
            }
        }
    }
    else
    {
        eStatus = MOS_STATUS_UNKNOWN;
    }

    return eStatus;
}

uint32_t MosUtilities::MosWaitForSingleObject(
    void                        *pObject,
    uint32_t                    uiMilliseconds)
{
    uint32_t WaitSignal = 0;
    MOS_UNUSED(pObject);
    MOS_UNUSED(uiMilliseconds);

    return WaitSignal;
}

uint32_t MosUtilities::MosWaitForMultipleObjects(
    uint32_t                    uiThreadCount,
    void                        **ppObjects,
    uint32_t                    bWaitAll,
    uint32_t                    uiMilliseconds)
{
    MOS_UNUSED(uiThreadCount);
    MOS_UNUSED(ppObjects);
    MOS_UNUSED(bWaitAll);
    MOS_UNUSED(uiMilliseconds);
    return 0;
}

int32_t MosUtilities::MosAtomicIncrement(
    int32_t *pValue)
{
    if (pValue != nullptr)
    {
        return __sync_add_and_fetch(pValue, 1);
    }
    return 0;
}

int32_t MosUtilities::MosAtomicDecrement(
    int32_t *pValue)
{
    if (pValue != nullptr)
    {
        return __sync_sub_and_fetch(pValue, 1);
    }
    return 0;
}

#ifndef WDDM_LINUX
VAStatus MosUtilities::MosStatusToOsResult(
    MOS_STATUS               eStatus)
{
    switch (eStatus)
    {
        case MOS_STATUS_SUCCESS:                        return VA_STATUS_SUCCESS;
        case MOS_STATUS_NO_SPACE:                       return VA_STATUS_ERROR_ALLOCATION_FAILED;
        case MOS_STATUS_INVALID_PARAMETER:              return VA_STATUS_ERROR_INVALID_PARAMETER;
        case MOS_STATUS_INVALID_HANDLE:                 return VA_STATUS_ERROR_INVALID_BUFFER;
        case MOS_STATUS_NULL_POINTER:                   return VA_STATUS_ERROR_INVALID_CONTEXT;
        default:                                        return VA_STATUS_ERROR_OPERATION_FAILED;
    }

    return VA_STATUS_ERROR_OPERATION_FAILED;
}

MOS_STATUS MosUtilities::OsResultToMOSStatus(
    VAStatus                 eResult)
{
    switch (eResult)
    {
        case VA_STATUS_SUCCESS:                     return MOS_STATUS_SUCCESS;
        case VA_STATUS_ERROR_ALLOCATION_FAILED:     return MOS_STATUS_NO_SPACE;
        case VA_STATUS_ERROR_INVALID_PARAMETER:     return MOS_STATUS_INVALID_PARAMETER;
        case VA_STATUS_ERROR_INVALID_BUFFER:        return MOS_STATUS_INVALID_HANDLE;
        case VA_STATUS_ERROR_INVALID_CONTEXT:       return MOS_STATUS_NULL_POINTER;
        default:                                    return MOS_STATUS_UNKNOWN;
    }

    return MOS_STATUS_UNKNOWN;
}
#else
MOS_OSRESULT MosUtilities::MosStatusToOsResult(
    MOS_STATUS eStatus)
{
    if (eStatus < MOS_STATUS_SUCCESS)
    {
        // It is in fact an HRESULT already, do nothing
        return (MOS_OSRESULT)eStatus;
    }

    switch (eStatus)
    {
    case MOS_STATUS_SUCCESS:
        return MOS_OSSOK;
    case MOS_STATUS_NO_SPACE:
        return MOS_OSEOUTOFMEMORY;
    case MOS_STATUS_INVALID_PARAMETER:
        return MOS_OSEINVALIDARG;
    case MOS_STATUS_INVALID_HANDLE:
        return MOS_OSEHANDLE;
    case MOS_STATUS_NULL_POINTER:
        return MOS_OSEPOINTER;
    case MOS_STATUS_UNIMPLEMENTED:
        return MOS_OSENOTIMPL;
    default:
        return MOS_OSEFAIL;
    }

    return MOS_OSEFAIL;
}

MOS_STATUS MosUtilities::OsResultToMOSStatus(
    MOS_OSRESULT eResult)
{
    switch (eResult)
    {
    case MOS_OSSOK:
        return MOS_STATUS_SUCCESS;
    case MOS_OSEOUTOFMEMORY:
        return MOS_STATUS_NO_SPACE;
    case MOS_OSEINVALIDARG:
        return MOS_STATUS_INVALID_PARAMETER;
    case MOS_OSEHANDLE:
        return MOS_STATUS_INVALID_HANDLE;
    case MOS_OSEPOINTER:
        return MOS_STATUS_NULL_POINTER;
    default:
        return MOS_STATUS_UNKNOWN;
    }

    return MOS_STATUS_UNKNOWN;
}
#endif
MOS_STATUS MosUtilities::MosGetLocalTime(
    struct tm* Tm)
{
    MOS_STATUS     eStatus = MOS_STATUS_SUCCESS;
    struct tm      *pTm;
    time_t         lTime = time(nullptr);
    pTm = localtime(&lTime);
    if(pTm == nullptr)
    {
        MOS_OS_ASSERTMESSAGE("Failed to get localtime.");
        eStatus = MOS_STATUS_UNKNOWN;
        return eStatus;
    }

    eStatus = MosSecureMemcpy(Tm, sizeof(struct tm), pTm, sizeof(struct tm));
    return eStatus;
}

void MosUtilities::MosTraceEventInit()
{
    char *val = getenv("GFX_MEDIA_TRACE");
    if (val)
    {
        MosUtilitiesSpecificNext::m_filterEnv = strtoll(val, nullptr, 0);
        val = getenv("GFX_MEDIA_TRACE_LEVEL");
        if (val)
        {
            MosUtilitiesSpecificNext::m_levelEnv = static_cast<uint32_t>(strtoll(val, nullptr, 0));
        }
        m_mosTraceEnable = true;
        m_mosTraceFilter = &MosUtilitiesSpecificNext::m_filterEnv;
        m_mosTraceLevel  = reinterpret_cast<uint8_t *>(&MosUtilitiesSpecificNext::m_levelEnv);
    }
    else
    {
        int fd = open(TRACE_SETTING_PATH, O_RDONLY);
        if (fd < 0)
        {
            return;
        }
        void *addr = mmap(NULL, TRACE_SETTING_SIZE, PROT_READ, MAP_SHARED, fd, 0);
        close(fd); // close first, map addr still valid after close
        if (addr == MAP_FAILED)
        {
            return;
        }
        m_mosTraceControlData = reinterpret_cast<const MtControlData *>(addr);

        m_mosTraceEnable = &m_mosTraceControlData->enable;
        m_mosTraceFilter = {
                        m_mosTraceControlData->filter,
                        sizeof(m_mosTraceControlData->filter) / sizeof(uint64_t)};
        m_mosTraceLevel = &m_mosTraceControlData->level;
    }

    // close first, if already opened.
    if (MosUtilitiesSpecificNext::m_mosTraceFd >= 0)
    {
        close(MosUtilitiesSpecificNext::m_mosTraceFd);
        MosUtilitiesSpecificNext::m_mosTraceFd = -1;
    }
    MosUtilitiesSpecificNext::m_mosTraceFd = open(MosUtilitiesSpecificNext::m_mosTracePath, O_WRONLY);
    return;
}

void MosUtilities::MosTraceEventClose()
{
    m_mosTraceEnable.Reset();
    m_mosTraceFilter.Reset();
    m_mosTraceLevel.Reset();
    if (m_mosTraceControlData)
    {
        munmap((void *)m_mosTraceControlData, TRACE_SETTING_SIZE);
        m_mosTraceControlData = nullptr;
    }
    if (MosUtilitiesSpecificNext::m_mosTraceFd >= 0)
    {
        close(MosUtilitiesSpecificNext::m_mosTraceFd);
        MosUtilitiesSpecificNext::m_mosTraceFd = -1;
    }
    MosUtilitiesSpecificNext::m_filterEnv = 0;
    MosUtilitiesSpecificNext::m_levelEnv  = {};
    return;
}

void MosUtilities::MosTraceSetupInfo(uint32_t DrvVer, uint32_t PlatFamily, uint32_t RenderFamily, uint32_t DeviceID)
{
    // not implemented
}

void MosUtilities::MosTraceEvent(
    uint16_t         usId,
    uint8_t          ucType,
    const void       *pArg1,
    uint32_t         dwSize1,
    const void       *pArg2,
    uint32_t         dwSize2)
{
    if (!m_mosTraceEnable)
    {
        return; // skip if trace not enabled from share memory
    }

    if (MosUtilitiesSpecificNext::m_mosTraceFd >= 0 &&
        TRACE_EVENT_MAX_SIZE > dwSize1 + dwSize2 + TRACE_EVENT_HEADER_SIZE)
    {
        uint8_t traceBuf[256];
        uint8_t *pTraceBuf = traceBuf;

        // special handling for media runtime log, filter by component
        if (usId == EVENT_MEDIA_LOG && pArg1 != nullptr && dwSize1 >= 2*sizeof(int32_t))
        {
            int32_t comp = (*(int32_t*)pArg1) >> 24;
            int32_t level = *((int32_t*)pArg1 + 1);

            if (!m_mosTraceFilter(TR_KEY_MOSMSG_ALL) || !m_mosTraceFilter(MEDIA_EVENT_FILTER_KEYID(comp + 16)))
            {
                // keyword not set for this component, skip it
                return;
            }
        }

        if (dwSize1 + dwSize2 + TRACE_EVENT_HEADER_SIZE > sizeof(traceBuf))
        {
            pTraceBuf = (uint8_t *)MOS_AllocAndZeroMemory(TRACE_EVENT_MAX_SIZE);
        }

        if (pTraceBuf)
        {
            // trace header
            uint32_t *header = (uint32_t *)pTraceBuf;
            uint32_t    nLen = TRACE_EVENT_HEADER_SIZE;

            header[0] = 0x494D5445; // IMTE (IntelMediaTraceEvent) as ftrace raw marker tag
            header[1] = (usId << 16) | (dwSize1 + dwSize2);
            header[2] = ucType;

            if (pArg1 && dwSize1 > 0)
            {
                MOS_SecureMemcpy(pTraceBuf+nLen, dwSize1, pArg1, dwSize1);
                nLen += dwSize1;
            }
            if (pArg2 && dwSize2 > 0)
            {
                MOS_SecureMemcpy(pTraceBuf+nLen, dwSize2, pArg2, dwSize2);
                nLen += dwSize2;
            }
            size_t writeSize = write(MosUtilitiesSpecificNext::m_mosTraceFd, pTraceBuf, nLen);
            if (traceBuf != pTraceBuf)
            {
                MOS_FreeMemory(pTraceBuf);
            }
        }
#if Backtrace_FOUND
        if (m_mosTraceFilter(TR_KEY_CALL_STACK))
        {
            // reserve space for header and stack size field.
            // max 32-2=30 layers call stack in 64bit driver.
            uint32_t nLen = 4*sizeof(uint32_t);
            void **stack = (void **)(traceBuf + nLen);
            int num = backtrace(stack, ((sizeof(traceBuf)-nLen)/sizeof(void *)));
            if (num > 0)
            {
                uint32_t *header = (uint32_t *)traceBuf;

                header[0] = 0x494D5445; // IMTE (IntelMediaTraceEvent) as ftrace raw marker tag
                header[1] = (EVENT_CALL_STACK << 16) | (num*sizeof(void *)+sizeof(uint32_t));
                header[2] = 0;
                header[3] = (uint32_t)num;
                nLen += num*sizeof(void *);
                size_t ret = write(MosUtilitiesSpecificNext::m_mosTraceFd, traceBuf, nLen);
            }
        }
#endif
    }
    return;
}

bool MosUtilities::MosShouldTraceEventMsg(
    uint8_t level,
    uint8_t compID)
{
    return false;
}

void MosUtilities::MosTraceEventMsg(
    uint8_t          level,
    uint8_t          compID,
    void             *message,
    void             *functionName,
    uint32_t         lineNum)
{
}

void MosUtilities::MosTraceDataDump(
    const char *pcName,
    uint32_t    flags,
    const void *pBuf,
    uint32_t    dwSize)
{
    if (MosUtilitiesSpecificNext::m_mosTraceFd >= 0 && pBuf && pcName)
    {
        uint8_t *pTraceBuf = (uint8_t *)MOS_AllocAndZeroMemory(TRACE_EVENT_MAX_SIZE);
        size_t writeSize = 0;

        if (pTraceBuf)
        {
            // trace header
            uint32_t *header = (uint32_t *)pTraceBuf;
            uint32_t    nLen = strlen(pcName) & 0xff;// 255 max pcName length

            header[0] = 0x494D5445; // IMTE (IntelMediaTraceEvent) as ftrace raw marker tag
            header[1] = (EVENT_DATA_DUMP << 16) | (8 + nLen + 1);
            header[2] = EVENT_TYPE_START;
            header[3] = dwSize;
            header[4] = flags;
            memcpy(&header[5], pcName, nLen);
            nLen += TRACE_EVENT_HEADER_SIZE + 8 + 1;
            writeSize = write(MosUtilitiesSpecificNext::m_mosTraceFd, pTraceBuf, nLen);
            // send dump data
            header[2] = EVENT_TYPE_INFO;
            const uint8_t *pData = static_cast<const uint8_t *>(pBuf);
            while (dwSize > 0)
            {
                uint32_t size = dwSize;
                if (size > TRACE_EVENT_MAX_DATA_SIZE)
                {
                    size = TRACE_EVENT_MAX_DATA_SIZE;
                }
                uint16_t len = ((size + 3) & ~3) / sizeof(uint32_t);
                uint8_t *pDst = (uint8_t *)&header[3];

                header[1] = (EVENT_DATA_DUMP << 16) | (size + sizeof(len));
                memcpy(pDst, &len, sizeof(len));
                memcpy(pDst+sizeof(len), pData, size);
                nLen = TRACE_EVENT_HEADER_SIZE + size + sizeof(len);
                writeSize = write(MosUtilitiesSpecificNext::m_mosTraceFd, pTraceBuf, nLen);
                dwSize -= size;
                pData += size;
            }
            // send dump end
            header[1] = EVENT_DATA_DUMP << 16;
            header[2] = EVENT_TYPE_END;
            writeSize = write(MosUtilitiesSpecificNext::m_mosTraceFd, pTraceBuf, TRACE_EVENT_HEADER_SIZE);

            MOS_FreeMemory(pTraceBuf);
        }
    }
}

void MosUtilities::MosTraceDataDictionary(
    const char* pcName,
    const void* pBuf,
    uint32_t    dwSize)
{
    // not implemented
}

MOS_STATUS MosUtilities::MosGfxInfoInit()
{
    // not implemented
    return MOS_STATUS_SUCCESS;
}

void MosUtilities::MosGfxInfoClose()
{
    // not implemented
}

void MosUtilities::MosGfxInfoRTErrInternal(uint8_t ver,
    uint16_t    compId,
    uint16_t    FtrId,
    uint32_t    ErrorCode,
    uint8_t     num_of_triples,
    va_list     args)
{
    // not implemented
}

void MosUtilities::MosGfxInfoRTErr(uint8_t ver,
    uint16_t    compId,
    uint16_t    FtrId,
    uint32_t    ErrorCode,
    uint8_t     num_of_triples,
    ...)
{
    // not implemented
}

void MosUtilities::MosGfxInfoInternal(
    uint8_t  ver,
    uint16_t compId,
    uint32_t tmtryID,
    uint8_t  num_of_triples,
    va_list  args)
{
    // not implemented
}

bool MosUtilities::MosIsProfilerDumpEnabled()
{
    return true;
}

void MosUtilities::MosGfxInfo(
    uint8_t         ver,
    uint16_t        compId,
    uint32_t        tmtryID,
    uint8_t         num_of_triples,
    ...)
{
    // not implemented
}

#if (_DEBUG || _RELEASE_INTERNAL)
void MosUtilities::MosMMPWriteFile(
    const std::string &name,
    const void        *data,
    size_t             size)
{
    std::ofstream ofs(name);
    ofs.write(static_cast<const char *>(data), size);
}
#endif  //(_DEBUG || _RELEASE_INTERNAL)
void PerfUtility::startTick(std::string tag)
{
    std::lock_guard<std::mutex> lock(perfMutex);
    Tick newTick = {};
    struct timespec ts = {};

    // get start tick count
    clock_gettime(CLOCK_REALTIME, &ts);
    newTick.start = int(ts.tv_sec * 1000000) + int(ts.tv_nsec / 1000); // us

    std::vector<Tick> *perf = nullptr;
    std::map<std::string, std::vector<Tick>*>::iterator it;
    it = records.find(tag);
    if (it == records.end())
    {
        perf = new std::vector<Tick>;
        perf->push_back(newTick);
        records[tag] = perf;
    }
    else
    {
        it->second->push_back(newTick);
    }
}

void PerfUtility::stopTick(std::string tag)
{
    std::lock_guard<std::mutex> lock(perfMutex);
    struct timespec ts = {};
    std::map<std::string, std::vector<Tick>*>::iterator it;
    it = records.find(tag);
    if (it == records.end())
    {
        // should not happen
        return;
    }

    // get stop tick count
    clock_gettime(CLOCK_REALTIME, &ts);
    it->second->back().stop = int(ts.tv_sec * 1000000) + int(ts.tv_nsec / 1000); // us

    // calculate time interval
    it->second->back().time = double(it->second->back().stop - it->second->back().start) / 1000.0; // ms
}

/*----------------------------------------------------------------------------
| Name      : GMMDebugBreak
| Purpose   : Fix compiling issue for Gmmlib on debug mode
| Arguments : N/A
| Returns   : void
| Calls     : N/A
| Callers   : Several
\---------------------------------------------------------------------------*/
void GMMDebugBreak(const char  *file, const char  *function,const int32_t line)
{
    // Not required for media driver
    return;
}

/*----------------------------------------------------------------------------
| Name      : GMMPrintMessage
| Purpose   : Fix compiling issue for Gmmlib on debug mode
| Arguments : N/A
| Returns   : void
| Calls     : N/A
| Callers   : Several
\---------------------------------------------------------------------------*/
void GMMPrintMessage(int32_t debuglevel, const char  *function, ...)
{
    // Not Required for media driver
    return;
}
